package cn.shuibo.advice;

import cn.shuibo.config.MutiSecretKeyConfig;
import cn.shuibo.dto.SecretDataDTO;
import cn.shuibo.entity.SecretKeyEntity;
import cn.shuibo.exception.CryptErrorException;
import cn.shuibo.util.*;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.stream.Collectors;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;

/**
 * Author:Bobby
 * DateTime:2019/4/9
 **/
public class DecryptHttpInputMessage implements HttpInputMessage {

    private Logger log = LoggerFactory.getLogger(this.getClass());
    private HttpHeaders headers;
    private InputStream body;


    public DecryptHttpInputMessage(HttpInputMessage inputMessage, MutiSecretKeyConfig mutiSecretKeyConfig) throws Exception {
        this.headers = inputMessage.getHeaders();
        String content = new BufferedReader(new InputStreamReader(inputMessage.getBody()))
                .lines().collect(Collectors.joining(System.lineSeparator()));
        String decryptBody;
        if (!content.startsWith("{") || !content.endsWith("}")) {
            log.info("Unencrypted without decryption:{}", content);
            throw new CryptErrorException("请求报文格式不正确");
        } else {
            SecretDataDTO cryptData = null;
            try {
                cryptData = JSONObject.parseObject(content, SecretDataDTO.class);//接受请求数据
            } catch (Exception e) {
                throw new CryptErrorException("请求报文格式换行异常，请检查报文格式是否正确");
            }

            if (cryptData == null || StringUtils.isBlank(cryptData.getAppId())) {
                throw new CryptErrorException("请求报文格式换行异常，请检查报文格式是否正确");
            }

            StringBuilder json = new StringBuilder();

            String appid = cryptData.getAppId();//用户标识
            String requestId = "";
            String sign = "";
            String random_key = "";
            Long time_stamp = 0L;
            try {
                content = cryptData.getContent();
                sign = cryptData.getSign();
                random_key = cryptData.getRandomKey();
                time_stamp = cryptData.getTimeStamp();
                requestId = cryptData.getRequestId();
            } catch (Exception e2) {
                String msg = "报文解密异常，请检查报文内容";
                log.error(msg, e2);
                throw new CryptErrorException(msg);
            }

            //todo: 时间戳超时可配置化，加入签名算法
            if (time_stamp + 5*60*1000 < System.currentTimeMillis()){
                throw new CryptErrorException("报文超时，请重新发送");
            }

            log.info("content=[{}]", content);
            log.info("sign=[{}]", sign);
            log.info("random_key=[{}]", random_key);

            SecretKeyEntity sender = mutiSecretKeyConfig.getByAppid(appid);

            if (sender == null) {
                throw new CryptErrorException("解密切面[De2EncryptAspect]--》请检查发送方或本机sender[" + appid + "]的配置数据是否已加载在内存中!");
            }

            String decode_content = "";
            String decode_sign = "";
            try {
                //用己方（接收方）私钥解密获取随机秘钥
                String decode_random_key = RSAUtils.decryptByPublicKey(random_key, sender.getPublicKey());
                //用随机秘钥解密加密原文，获取源数据
                decode_content = AESUtils.decrypt(content, decode_random_key);
                //用发送方的公钥解密获取发送方消息摘要
                decode_sign = RSAUtils.decryptByPublicKey(sign, sender.getPublicKey()); //签名无需加密，RSAUtils.decryptByPublicKey(sign, sender.getPublicKey());
                //打印解密日志
                if (mutiSecretKeyConfig.isShowLog()) {
                    log.info("随机秘钥加密原文:[{}],解密后原文:[{}]", random_key, decode_random_key);
                    log.info("源数据加密原文:[{}],解密后原文:[{}]", content, decode_content);
                    log.info("数字签名加密原文:[{}],解密后原文:[{}]", sign, decode_sign);
                }
            } catch (Exception e1) {
                log.error("解密切面[De2EncryptAspect]--》解密失败", e1);
                throw new CryptErrorException("解密切面[De2EncryptAspect]--》解密失败[" + e1.getMessage() + "]");
            }
            //验证消息摘要，判断是否被篡改，一致则验证通过 ,时间戳参与签名
            boolean isVerifi = MD5Util.MD5Encode(decode_content + time_stamp).equals(decode_sign);
            if (!isVerifi) {
                log.error("原文摘要:[{}],发送方摘要:[{}]", MD5Util.MD5Encode(decode_content), decode_sign);
                throw new CryptErrorException("解密切面[De2EncryptAspect]--》验签失败!");
            }
            if (mutiSecretKeyConfig.isShowLog()) {
                log.info("Encrypted data received：{},After decryption：{}", content);
            }
            this.body = new ByteArrayInputStream(decode_content.getBytes());
        }
    }

    @Override
    public InputStream getBody() {
        return body;
    }

    @Override
    public HttpHeaders getHeaders() {
        return headers;
    }
}
